#ifndef JSGLUE_H
#define JSGLUE_H

#include <Ogre.h>
#include <v8.h>

#include "gameobjects/GameObject.h"

using namespace v8;

void js_do_file(v8::Context::Scope context_scope, const char* name);

namespace JSGlue
{
	typedef struct js_reg
	{
		const char *name;
		InvocationCallback func;
	} js_reg;

	void glue(Persistent<Context> ctx);

	template<typename T>
	Handle<v8::Object> wrap(void *v)
	{
		v8::HandleScope handle_scope;

		Handle<ObjectTemplate> objTempl = ObjectTemplate::New();
		objTempl->SetInternalFieldCount(1);

		Handle<v8::Object> result = objTempl->NewInstance();
		Handle<External> ptr = External::New(static_cast<T*>(v));
		result->SetInternalField(0, ptr);

		return handle_scope.Close(result);
	}

	template<typename T>
	T* unwrap(v8::Handle<v8::Object> obj)
	{
		v8::Handle<v8::External> field;
		field = v8::Handle<v8::External>::Cast(obj->GetInternalField(0));
		void* ptr = field->Value();
		return static_cast<T*>(ptr);
	}

//    void Expose(v8::Handle<v8::Object> intoObject, v8::Handle<v8::Value> namev8String, v8::InvocationCallback funcID)
//    {
//        v8::HandleScope handle_scope;
//        v8::Handle<v8::FunctionTemplate> thisFunc;
//        thisFunc = v8::FunctionTemplate::New(funcID);
//        intoObject->Set(namev8String, thisFunc->GetFunction());
//    }




	class Vector3
	{
	public:
		static Handle<FunctionTemplate> templ;
		static Handle<ObjectTemplate> proto;
		static Handle<ObjectTemplate> inst;
		
		static const js_reg methods[];

		static v8::Handle<v8::Value> create(const v8::Arguments &args);
		static void gc(Persistent<v8::Object> self, void* parameter);
		static void glue(Persistent<Context> ctx);
		static Handle<v8::Object> wrap(void *v);
		static Ogre::Vector3* unwrap(v8::Handle<v8::Object> obj);
		static Handle<v8::Value> toString(const Arguments& args);
		static Handle<v8::Value> length(const Arguments& args);
		static Handle<v8::Value> copy(const Arguments& args);
		static Handle<v8::Value> add(const Arguments& args);
		static Handle<v8::Value> mul(const Arguments& args);

		static Handle<v8::Value> Get(Local<v8::String> property, const AccessorInfo& info)
		{
			Local<v8::Object> self = info.Holder();
			Local<External> external = Local<External>::Cast(self->GetInternalField(0));
			Ogre::Vector3* v = static_cast<Ogre::Vector3*>(external->Value());

			v8::String::AsciiValue p(property);
			if(strcmp(*p, "x")==0) return v8::Number::New(v->x);
			if(strcmp(*p, "y")==0) return v8::Number::New(v->y);
			if(strcmp(*p, "z")==0) return v8::Number::New(v->z);
			return v8::Undefined();
		}

		static void Set(Local<v8::String> property, Local<v8::Value> value, const AccessorInfo& info)
		{
			Local<v8::Object> self = info.Holder();
			Local<External> external = Local<External>::Cast(self->GetInternalField(0));
			Ogre::Vector3* v = static_cast<Ogre::Vector3*>(external->Value());

			v8::String::AsciiValue p(property);
			if(strcmp(*p, "x")==0) v->x = value->NumberValue();
			if(strcmp(*p, "y")==0) v->y = value->NumberValue();
			if(strcmp(*p, "z")==0) v->z = value->NumberValue();
		}

		static Handle<v8::Value> addStatic(const Arguments& args)
		{
			if (args.Length() != 2) return ThrowException(v8::String::New("Expected two arguments"));

			Ogre::Vector3* u;
			Ogre::Vector3* v;

			v8::Local<v8::External> ext = v8::Local<v8::External>::Cast(args[0]->ToObject()->GetInternalField(0));
			u = static_cast<Ogre::Vector3*>(ext->Value());

			ext = v8::Local<v8::External>::Cast(args[1]->ToObject()->GetInternalField(0));
			v = static_cast<Ogre::Vector3*>(ext->Value());

			std::cout << *v << std::endl;
			std::cout << *u << std::endl;

			return v8::Undefined();
		}



	};

	class Object
	{
	public:
		static Handle<FunctionTemplate> templ;
		static Handle<ObjectTemplate> proto;
		static Handle<ObjectTemplate> inst;

		static void glue(Persistent<Context> ctx);
		static ::Object* unwrap(v8::Handle<v8::Object> obj);
		static Handle<v8::Object> wrap(void *o);

		static Handle<v8::Value>        MapGet(Local<v8::String> name, const AccessorInfo &info);
		static Handle<v8::Value>        MapSet(Local<v8::String> name, Local<v8::Value> value, const AccessorInfo& info);

	};

	class GameObject
	{
	public:
		static Handle<FunctionTemplate> templ;
		static Handle<ObjectTemplate> proto;
		static Handle<ObjectTemplate> inst;

		static void                 glue(Persistent<Context> ctx);

		static Handle<v8::Value>        create(const Arguments &args);
		static void                 gc(Persistent<v8::Object> self, void* parameter);

		static Handle<v8::Object>   wrap(::GameObject *go);
		static ::GameObject*        unwrap(v8::Handle<v8::Object> obj);

		static Handle<v8::Value>        MapGet(Local<v8::String> name, const AccessorInfo &info);
		static Handle<v8::Value>        MapSet(Local<v8::String> name, Local<v8::Value> value, const AccessorInfo& info);

		static Handle<v8::Value>        getByName(const Arguments& args);
		static Handle<v8::Value>        toString(const Arguments& args);
	};


}
#endif // JSGLUE_H
